/*
 *  tf_renderer.cpp
 *  Perceel
 *
 *  Created by Nicola Gigante on 20/03/08.
 *  Copyright 2008 Nicola Gigante. All rights reserved.
 *
 */

#include "tf_renderer.h"

TFRenderer::TFRenderer(QWidget *parent) : QWidget(parent)
{
	setMouseTracking(true);
	m_selectedRoot = -1;
	m_selectedPole = -1;
	m_border = 10;
}

TFRenderer::TFRenderer(const QStyleOptionViewItem &item)
{
	setMouseTracking(false);
	m_selectedRoot = -1;
	m_selectedPole = -1;
	m_border = 10;
	
	this->setFont(item.font);
	this->setPalette(item.palette);
}

void TFRenderer::setTF(TF tf)
{
	m_tf = tf;
	if(m_selectedRoot >= m_tf.roots().count())
		setSelectedRoot(-1);
	if(m_selectedPole >= m_tf.poles().count())
		setSelectedPole(-1);
	updateGeometry();
	update();
}

void TFRenderer::setSelectedRoot(int s)
{
	if(m_selectedRoot != s)
	{
		m_selectedRoot = s;
		m_selectedPole = -1;
		emit selectionChanged();
	}
}

void TFRenderer::setSelectedPole(int s)
{
	if(m_selectedPole != s)
	{
		m_selectedPole = s;
		m_selectedRoot = -1;
		emit selectionChanged();
	}
}

void TFRenderer::paintEvent(QPaintEvent *)
{
	QPainter painter(this);
	
	painter.fillRect(rect(), Qt::white);
	
	rootsAreas.clear();
	polesAreas.clear();
	QPicture p = paintTF(&rootsAreas, &polesAreas);
	
	painter.drawPicture(m_border,m_border,p);
}

QSize TFRenderer::sizeHint() const
{
	return paintTF().boundingRect().size() + QSize(m_border*2, m_border*2);
}

void TFRenderer::mousePressEvent(QMouseEvent *event)
{
	bool selected = false;
	
	if(event->button() == Qt::LeftButton)
	{
		QPoint p = event->pos() - QPoint(m_border, m_border);
		for(int i = 0; i < rootsAreas.count(); i++)
			if(rootsAreas[i].contains(p))
			{
				setSelectedRoot(i);
				selected = true;
				break;
			}
		for(int i = 0; i < polesAreas.count(); i++)
			if(polesAreas[i].contains(p))
			{
				setSelectedPole(i);
				selected = true;
				break;
			}
	}
	if(!selected)
		setSelectedRoot(-1);
	
	update();
}

void TFRenderer::mouseMoveEvent(QMouseEvent *event)
{
	QPoint p = event->pos() - QPoint(m_border, m_border);
	bool hover = false;
	
	foreach(QRect rect, rootsAreas)
		if(rect.contains(p))
			hover = true;
	foreach(QRect rect, polesAreas)
		if(rect.contains(p))
			hover = true;
	
	if(hover)
		setCursor(Qt::PointingHandCursor);
	else
		unsetCursor();
}

QPicture TFRenderer::paintTF(QList<QRect> *rootsAreas, QList<QRect> *polesAreas) const
{
	QList<Complex> roots;
	QList<Complex> poles;
	
	QPicture tfPicture;
	QPainter painter(&tfPicture);
	QPoint point(0, rect().height() / 2); // Initial point. The center of the screen at the start of the line
	QFontMetrics metrics(font());
	
	QString tfName = "G(s) = "; // Setup the transfer function's name
		
	QRect bRect = metrics.boundingRect(tfName); // Setup the initial bounding rectangle
	
	// Move the initial point to the vertial center of the picture
	if(m_tf.isFract())
		bRect.moveTop(FRACTLINE + 1);
	else
		bRect.moveTop(1);
	
	point = bRect.bottomLeft();
	painter.drawText(point, tfName); // Draw the name
	point = bRect.bottomRight(); // Advance the drawing point
	
	QPoint fractionStart = point - QPoint(0, FRACTLINE / 2); // Save the point before the fractionLine
	
	// Numerator
	if(m_tf.isFract())		// If the transfer function has a denominator
		point.ry() -= FRACTLINE;	// Put the numerator over the fraction line
	
	for(int i = 0; i < m_tf.roots().count(); i++) // For each root
	{
		if(roots.contains(m_tf.roots()[i]))		  // Skip if already drawn
		{
			if(rootsAreas)
				rootsAreas->append(QRect());			// Null rectangle for the selection handling
			continue;
		}
		roots.append(m_tf.roots()[i]);		// Else add to the drawn list
		
		int exp = m_tf.roots().count(m_tf.roots()[i]); // Exponent to draw over the bracket
		
		QPicture pict = drawRoot(m_tf.roots()[i], exp, point); // Partial picture of the root
		
		if(rootsAreas)
			rootsAreas->append(pict.boundingRect()); // Add the rectangle to the selectable areas
		
		if(m_selectedRoot == i)
			painter.fillRect(pict.boundingRect(), palette().highlight());
		
		bRect |= pict.boundingRect(); // Set up the bounding rectangle
		point = pict.boundingRect().bottomRight(); // Advance the drawingPoint
		
		painter.drawPicture(0, 0, pict); // Actually draw the picture
	}
	
	// Denominator
	if(m_tf.isFract())
	{
		point = fractionStart; // Reset the drawing point
		
		for(int i = 0; i < m_tf.poles().count(); i++) // For each pole
		{
			if(poles.contains(m_tf.poles()[i]))		  // Skip if already drawn
			{
				if(polesAreas)
					polesAreas->append(QRect());		// Null rectangle for the selection handling
				continue;
			}
			poles.append(m_tf.poles()[i]);		// Else add to the drawn list
			
			int exp = m_tf.poles().count(m_tf.poles()[i]); // Exponent to draw over the bracket
			
			QPicture pict = drawRoot(m_tf.poles()[i], exp, point, Qt::AlignTop); // Partial picture of the pole
			
			if(polesAreas)
				polesAreas->append(pict.boundingRect()); // Add the rectangle to the selectable areas
			
			if(m_selectedPole == i)
				painter.fillRect(pict.boundingRect(), palette().highlight());
			
			bRect |= pict.boundingRect(); // Set up the bounding rectangle
			point = pict.boundingRect().topRight(); // Advance the drawingPoint
			
			painter.drawPicture(0, 0, pict); // Actually draw the picture
		}
		
		painter.drawLine(fractionStart, QPoint(bRect.right(), fractionStart.y())); // Draw the fraction line
	}
	
	painter.end();
	tfPicture.setBoundingRect(bRect);
	return tfPicture;
}

QPicture TFRenderer::drawRoot(const Complex &root, int exponent, QPointF start, Qt::Alignment align) const
{
	QString str = " s";
	
	if(real(root) != 0)
		str += QString(" %1 %2 ").arg(-real(root) > 0 ? '+' : '-').arg(fabsl(real(root)), 0, 'g', 3);
	
	if(imag(root) != 0)
		str += QString(" %1 j%2 ").arg(-imag(root) > 0 ? '+' : '-').arg(fabsl(imag(root)), 0, 'g', 3);
	
	
	if(str != " s")
		str = "(" + str + ")";
	
	QString exp_str = exponent != 1 ? QString::number(exponent) : "";
	
	QFontMetrics metrics(font());
	QSize pictSize = QSize(metrics.width(str) + metrics.width(exp_str), metrics.boundingRect(str).height() * (exponent == 1 ? 1 : 1.5));
	
	QPicture picture;
	QPointF drawingPoint = start;
	if(align == Qt::AlignBottom)
		drawingPoint -= QPoint(0, pictSize.height());
	picture.setBoundingRect(QRectF(drawingPoint, pictSize).toRect());
	QPainter painter(&picture);
	
	painter.drawText(picture.boundingRect().bottomLeft(), str);
	if(exponent != 1)
		painter.drawText(start.x() + metrics.width(str), picture.boundingRect().bottom() - metrics.xHeight(), exp_str);
	
	painter.end();
	return picture;
}
